package xexcel

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/tealeg/xlsx/v3"
	"reflect"
	"strings"
)

type XExcel[T any] struct {
	Data   []*T // 数据
	Name   string
	Fields []*XExcelField
}

func NewXExcel[T any](name string) *XExcel[T] {
	excel := &XExcel[T]{
		Name:   name,
		Fields: getExcelFields(new(T)),
	}
	return excel
}
func (x *XExcel[T]) SetData(data []*T) *XExcel[T] {
	x.Data = data
	return x
}

func (x *XExcel[T]) Export(ctx *gin.Context, data []*T) {
	x.SetData(data)
	// 创建一个新的Excel文件
	file := xlsx.NewFile()
	// 添加一个工作表
	sheet, err := file.AddSheet("Sheet1")
	if err != nil {
		panic(err)
	}
	fields := x.Fields
	if fields == nil || len(fields) == 0 {
		return
	}
	fmt.Printf("%s", fields)
	datas := getExcelStringValues(x.Data, x.Fields)
	if len(datas) > 0 {
		fmt.Printf("%d", len(datas))
	}
	// 添加表头
	row := sheet.AddRow()
	// 设置表头样式
	row.SetHeight(36.0)

	// 创建并配置样式
	style := xlsx.NewStyle()
	fill := *xlsx.NewFill("solid", "00FF0000", "FF000000")
	style.Fill = fill
	border := *xlsx.NewBorder("thin", "thin", "thin", "thin")
	style.Border = border

	for _, header := range fields {
		cell := row.AddCell()
		cell.SetString(header.Name)
		cell.SetStyle(style)
	}
	for _, dataRow := range datas {
		row = sheet.AddRow()
		for _, dataVal := range dataRow {
			cell := row.AddCell()
			cell.SetString(dataVal)
		}
	}

	// 设置响应头，提示浏览器下载
	ctx.Header("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
	ctx.Header("Content-Disposition", "attachment; filename=output.xlsx")

	// 将文件写入响应体
	err = file.Write(ctx.Writer)
	if err != nil {
		ctx.AbortWithStatusJSON(500, gin.H{"error": err.Error()})
	}
}
func getExcelHeaders(model interface{}) ([]string, []string) {
	header := make([]string, 0)
	names := make([]string, 0)

	val := reflect.ValueOf(model).Elem()
	typ := val.Type()

	for i := 0; i < typ.NumField(); i++ {
		field := typ.Field(i)
		tag := field.Tag.Get("name")
		if tag != "" {
			header = append(header, tag)
			names = append(names, field.Name)
		}
	}
	return header, names
}

// getExcelFields 根据给定的模型获取Excel字段信息列表。
// 该函数使用反射来遍历模型的字段，并根据字段的"name"标签来创建XExcelField对象。
// 参数:
//
//	model - 一个接口类型的参数，代表需要反射的模型结构体。
//
// 返回值:
//
//	[]*XExcelField - 一个XExcelField指针的切片，每个元素包含模型每个字段的信息。
func getExcelFields(model interface{}) []*XExcelField {
	// 初始化一个空的XExcelField指针切片来存储字段信息。
	excelFields := make([]*XExcelField, 0)

	// 使用反射获取模型的Value和Type信息。
	val := reflect.ValueOf(model).Elem()
	typ := val.Type()

	// 遍历模型的所有字段。
	for i := 0; i < typ.NumField(); i++ {
		// 获取当前字段的Type信息。
		field := typ.Field(i)

		// 从字段的Tag中提取"name"标签的值。
		tag := field.Tag.Get("name")

		// 使用字段名和Tag中的"name"值来创建一个新的XExcelField对象。
		excelField := NewXExcelField(field.Name, tag)
		readConverterExp := field.Tag.Get("readConverterExp")
		excelField.Separator = field.Tag.Get("separator")
		if readConverterExp != "" {
			excelField.ReadConverterExp = readConverterExp
			dicts := strings.Split(readConverterExp, ",")
			if dicts != nil && len(dicts) > 0 {
				readConverterExpMap := make(map[string]string)
				for _, dict := range dicts {
					dx := strings.Split(dict, "=")
					if len(dx) == 2 {
						readConverterExpMap[dx[0]] = dx[1]
					}
				}
				excelField.ReadConverterExpMap = readConverterExpMap
			}
		}
		excelField.DictType = field.Tag.Get("dictType")
		excelField.Prompt = field.Tag.Get("prompt")
		excelField.DefaultValue = field.Tag.Get("defaultValue")

		// 将新创建的XExcelField对象添加到切片中。
		excelFields = append(excelFields, excelField)
	}

	// 返回包含所有字段信息的切片。
	return excelFields
}

func getExcelValues[T any](models []*T, names []string) [][]interface{} {
	result := make([][]interface{}, 0)
	for _, model := range models {
		val := reflect.ValueOf(model).Elem()
		values := make([]interface{}, 0)
		for _, name := range names {
			_, have := val.Type().FieldByName(name)
			if have {
				value := val.FieldByName(name).Interface()
				values = append(values, value)
			}
		}
		result = append(result, values)
	}
	return result
}
func getExcelStringValues[T any](models []*T, fields []*XExcelField) [][]string {
	result := make([][]string, 0)
	for _, model := range models {
		val := reflect.ValueOf(model).Elem()
		values := make([]string, 0)
		for _, field := range fields {
			_, have := val.Type().FieldByName(field.Field)
			if have {
				value := val.FieldByName(field.Field).Interface()
				// 转换为字符串
				var str string
				if value == nil {
					str = ""
				} else {
					// 使用反射处理指针、基础类型等
					v := reflect.ValueOf(value)
					if v.Kind() == reflect.Ptr && !v.IsNil() {
						v = v.Elem()
					}
					switch v.Kind() {
					case reflect.String:
						str = v.String()
					case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
						str = fmt.Sprintf("%d", v.Int())
					case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
						str = fmt.Sprintf("%d", v.Uint())
					case reflect.Float32, reflect.Float64:
						str = fmt.Sprintf("%v", v.Float())
					case reflect.Bool:
						str = fmt.Sprintf("%t", v.Bool())
					default:
						str = fmt.Sprintf("%v", v.Interface())
					}
					if str == "<nil>" {
						str = ""
					}
				}
				if field.ReadConverterExp != "" {
					if v, ok := field.ReadConverterExpMap[str]; ok {
						str = v
					}
				}
				if field.Suffix != "" {
					str = str + field.Suffix
				}
				values = append(values, str)
			}
		}
		result = append(result, values)
	}
	return result
}
